package com.example.demo.utils;

import org.apache.commons.codec.binary.Base64;
import org.springframework.util.StringUtils;

import javax.crypto.Cipher;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateFactory;
import java.util.Enumeration;

public class RSAUtil {
	/**
	 * RSA最大加密明文大小
	 */
	private static final int MAX_ENCRYPT_BLOCK = 117;

	/**
	 * RSA最大解密密文大小
	 */
	private static final int MAX_DECRYPT_BLOCK = 256;
	private static final String CIPHER_STR = "RSA";

	private static Base64 base64 = new Base64();

	/**
	 * 银行私钥
	 */
	private static PrivateKey privateBankKey;

	/**
	 * 银行公钥
	 */
	private static PublicKey publicBankKey;

	/**
	 * 税局公私钥
	 */
	private static PublicKey taxPubKey;
	private static PrivateKey taxPriKey;

	private static RSAUtil rsaUtil = new RSAUtil();
	private RSAUtil() {
	}
	public static RSAUtil newInstance() {
		return rsaUtil;
	}

	public static PrivateKey getPrivateBankKey() {
		return privateBankKey;
	}
	public static PublicKey getPublicBankKey() {
		return publicBankKey;
	}
	public static PublicKey getTaxPubKey() {
		return taxPubKey;
	}
	public static PrivateKey getTaxPriKey() {
		return taxPriKey;
	}

	/**
	 * 公钥加密
	 *
	 * @param dataStr   需要加密的数据
	 * @param publicKey 公钥
	 * @return 使用公钥加密后的数据
	 * @throws Exception
	 */
	public static String rsaEncrypt(PublicKey publicKey,String dataStr) throws Exception {
		byte[] encryptedData = null;
		try {
			byte[] data = dataStr.getBytes();
			encryptedData = cipherDoFinal(publicKey, Cipher.ENCRYPT_MODE, data, MAX_ENCRYPT_BLOCK);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return encryptedData == null ? null : new String(base64.encode(encryptedData), StandardCharsets.UTF_8);
	}

	/**
	 * 用私钥对数据进行解密
	 *
	 * @param privateKey 私钥
	 * @return 解密后的数据
	 * @throws Exception
	 */
	public static String rsaDecrypt(PrivateKey privateKey,String dataStr) throws Exception {
		byte[] decryptedData = null;
		try {
			byte[] data = base64.decode(dataStr.getBytes());
			decryptedData = cipherDoFinal(privateKey, Cipher.DECRYPT_MODE, data, MAX_DECRYPT_BLOCK);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return decryptedData == null ? null : new String(decryptedData, StandardCharsets.UTF_8);
	}

	/**
	 * 根据key和 mode进行加密和解密
	 * @param key
	 * @param mode
	 * @param data
	 * @param maxBlock
	 * @return
	 * @throws Exception
	 */
	private static byte[] cipherDoFinal(Key key, int mode, byte[] data, int maxBlock) throws Exception {
		ByteArrayOutputStream out = null;
		try {
			Cipher cipher = Cipher.getInstance(CIPHER_STR);
			cipher.init(mode, key);
			int inputLen = data.length;
			out = new ByteArrayOutputStream();
			int offSet = 0;
			byte[] cache;
			int i = 0;
			while (inputLen - offSet > 0) {
				if (inputLen - offSet > maxBlock) {
					cache = cipher.doFinal(data, offSet, maxBlock);
				} else {
					cache = cipher.doFinal(data, offSet, inputLen - offSet);
				}
				out.write(cache, 0, cache.length);
				i++;
				offSet = i * maxBlock;
			}
		}catch(Exception e){
			e.printStackTrace();
		}finally {
			if(out != null){
				out.close();
			}
		}
		return out == null ? null : out.toByteArray();
	}

	/**
	 * 根据pfx证书路径和密码获取银行私钥
	 * @param pfxCertPath
	 * @param certPassword
	 * @return
	 */
	private PrivateKey getPrivateKey(String pfxCertPath, String certPassword) throws Exception{
		if(null != privateBankKey){
			return privateBankKey;
		}
		if (pfxCertPath == null || pfxCertPath.length() <= 0) {
			return null;
		}

		if (certPassword == null || "".equals(certPassword.trim())) {
			return null;
		}

		FileInputStream fis = null;
		PrivateKey privateKey = null;
		try {
			KeyStore ks = KeyStore.getInstance("PKCS12");
			fis = new FileInputStream(pfxCertPath);
			char[] nPassword = certPassword.toCharArray();
			ks.load(fis, nPassword);
			Enumeration enumas = ks.aliases();
			String keyAlias = null;
			if (enumas.hasMoreElements()) {
				keyAlias = (String) enumas.nextElement();
			}
			privateKey = (PrivateKey) ks.getKey(keyAlias, nPassword);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				fis.close();
			}
		}
		return privateKey;
	}

	/**
	 * 根据crt证书路径获取银行公钥
	 * @param filePath
	 * @return
	 * @throws Exception
	 */
	private PublicKey getPublicKey(String filePath) throws Exception {
		if(publicBankKey != null){
			return publicBankKey;
		}
		java.security.cert.Certificate cert = CertificateFactory.getInstance("X.509")
				.generateCertificate(new FileInputStream(filePath));

		return cert.getPublicKey();
	}

	public void initTaxPublicKey(String filePath) throws Exception {
		taxPubKey = getPublicKey(filePath);
	}

	/**
	 * 初始化银行公钥
	 * @param fileNameWithEndffix
	 */
	public void initBankPublicKey(String fileNameWithEndffix) throws Exception {
		publicBankKey = getPublicKey(fileNameWithEndffix);
	}

	/**
	 * 初始化 私钥
	 */
	public void initBankPrivateKey(String filePath,String password) throws Exception {
		privateBankKey = getPrivateKey(filePath, password);
	}

	public static String readTxtFile(String filePath,String charset) {
		//校验参数
		if (StringUtils.isEmpty(filePath)) {
			return null;
		}
		StringBuffer stringBuffer = new StringBuffer();//用来拼接字符串
		try {
			//获取文件
			File file = new File(filePath);
			//获取文件的读取器
			BufferedReader reader = new BufferedReader(
					new InputStreamReader(
							new FileInputStream(file), charset));
			//读取文件
			String tempStr;
			while ((tempStr = reader.readLine()) != null) {
				stringBuffer.append(tempStr+"\n");//[\n]换行
			}
			//关闭读取器
			if (reader != null) {
				reader.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
		//返回
		return stringBuffer.toString();
	}

}
